using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Pbdms.Common.SharedVariables;
using Pbdms.Common.CryptoUtils;
using Pbdms.Common;
using Pbdms.Common.DataObject;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Bcpg;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace Pbdms.NetworkDealer
{
    public class AuthTcpClient
    {
        private TcpClient tcpClient;
        private SmartNetworkStream NetStream;
        private PublicKeysControl publicKeyControl;
        private SecretKeyControl secretKeyControl;
        private FriendsControl friendsControl;
        private int port;
        private string friendKeyID;
        private string clientIP;
        Boolean Authenticated=false;
        public AuthTcpClient(string clientIP,int port, string friendKeyID,SecretKeyControl secretKeyControl, PublicKeysControl publicKeyControl,FriendsControl friendsControl)
        {
            this.clientIP=clientIP;
            this.port=port;
            this.friendKeyID = friendKeyID;
            this.publicKeyControl = publicKeyControl;
            this.secretKeyControl = secretKeyControl;
            this.friendsControl = friendsControl;
            
        }
        public AuthTcpClient(TcpClient tcpClient,int port,string friendKeyID,Boolean authenticate,SecretKeyControl secretKeyControl, PublicKeysControl publicKeyControl, FriendsControl friendsControl)
        {
            this.tcpClient = tcpClient;
            this.NetStream = new SmartNetworkStream(tcpClient);
            this.port = port;
            this.friendKeyID = friendKeyID;
            this.friendsControl = friendsControl;
            this.publicKeyControl = publicKeyControl;
            this.secretKeyControl = secretKeyControl;
            this.Authenticated = authenticate;
        }
        public void Connect()
        {

                tcpClient = new TcpClient(clientIP, port);
                NetStream = new SmartNetworkStream(tcpClient);
               
               

                //once connected start handshaking
                startHandShaking();

        }
        
        public Boolean Connected()
        {
            if (tcpClient != null)
            {
                return tcpClient.Connected;
            }
            return false;
        }
        public string getPeerID()
        {
            return friendKeyID;
        }
        public Boolean IsAuthenticated()
        {
            return Authenticated;
        }
        public void Close()
        {
            if (NetStream != null)
            {
                NetStream.Close();
            }
            if (tcpClient != null)
            {
                tcpClient.Close();
                
            }
        }
        public void sendMessage(NetworkMessage message)
        {
            MemoryStream outputMemory = new MemoryStream();
            try
            {
                ///convert to memory
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(outputMemory, message);
              
                byte[] data = outputMemory.ToArray();  
                NetStream.Write(data, 0, data.Length);
            }
            catch
            {
                throw new Exception("error");
            }
            finally
            {
                //close memory
                if (outputMemory != null)
                {
                    outputMemory.Close();
                }
            }            
        }        
        public void sendBytes(byte [] data)
        {
         

            NetStream.Write(data, 0, data.Length);
             
        }
        public NetworkMessage receiveMessage()
        {
        

            byte[] data = NetStream.Read();
            MemoryStream outputMemory = new MemoryStream();
            try
            {
                
                outputMemory.Write(data, 0, data.Length);
                outputMemory.Seek(0, SeekOrigin.Begin);
                BinaryFormatter formatter = new BinaryFormatter();
                NetworkMessage message = (NetworkMessage)formatter.Deserialize(outputMemory);
                message.SenderKeyID = friendKeyID;
                return message;
            }
            catch
            {
                throw new Exception("error");
            }
            finally
            {
                outputMemory.Close();
            }
             
           
        }
        public byte[] receiveBytes()
        {
            return NetStream.Read();
        }


        public SmartNetworkStream getStream()
        {
            return NetStream;
        }
        /// <summary>
        /// start handshaking
        /// </summary>
        private void startHandShaking()
        {
            sendMessageOne();        
        }
        /// <summary>
        /// send message one
        /// </summary>
        private void sendMessageOne()
        {
            // generate Na 
            string NaGenerated=Common.Global.RandomString();
            Signer signer = new Signer();
            string encryptedSignedMessage = "trashEncryptedMessage1";            
            try
            {
                //sign Na and produce a signature with Na
                string signedMessage = signer.SignAMessage(NaGenerated, secretKeyControl.readSecretKey(), secretKeyControl.readPrivateKey());
                Encrypter encrypter = new Encrypter();
                //encrypt it
                encryptedSignedMessage = encrypter.EncryptString(signedMessage, publicKeyControl.getPublicKey(friendKeyID), true);
            }
            catch
            {
                //send fake message 1
                //signal that this is unauthentication connection
                encryptedSignedMessage = "trashEncryptedMessage1"; 
                friendKeyID = "";
            }
           
            byte[] data = System.Text.Encoding.ASCII.GetBytes(encryptedSignedMessage);
            NetStream.Write(data, 0, data.Length);
            // start to receive message two
            receiveMessageTwo(NaGenerated);           
        }

        /// <summary>
        /// receive message two
        /// </summary>
        /// <param name="NaGenerated"></param>
        private void receiveMessageTwo( string NaGenerated)
        {
            byte []data=NetStream.Read();
            string message = System.Text.Encoding.ASCII.GetString(data);
            string NaReceived = "trashReceivedNa";
            string NbReceived = "trashReceivedNb";
            try
            {
                ///decrypt and verify the message 
                Decrypter decrypter = new Decrypter();
                string decryptMessage = decrypter.DecryptMessage(message, secretKeyControl.readPrivateKey());
                Verifier verifier = new Verifier(publicKeyControl);
                Common.DataObject.VerifyObj verifyObj = verifier.VerifyMessage(decryptMessage);
                //check whether the keyid matches the one sent
                if (verifyObj.keyID.Equals(friendKeyID))
                {
                    string[] arg = verifyObj.Data.Split(',');
                    NaReceived = arg[0];
                    NbReceived = arg[1];
                }
                else
                {
                    //signal that this is unauthentication connection
                    throw new Exception();
                }
            }
            catch
            {
                ///signal that this is unauthentication connection
                NaReceived = "";
                NbReceived = " trashMessage";
            }
            finally
            {
                if (NaReceived.Equals(NaGenerated))
                {
                    //signal that this is authentication connection
                    Authenticated = true;
                }
                else
                {
                    //signal that this is unauthentication connection
                    friendKeyID = "";
                    Authenticated = false;
                }                
            }
            sendMessageThree(NbReceived);
        }
        /// <summary>
        /// start sending message three
        /// </summary>
        /// <param name="NbReceived"></param>
        private void sendMessageThree(string NbReceived)
        {
            string encryptedSignedMessage = "trashEncryptedMessage3";
            try
            {
                //sign the Nb received
                Signer signer = new Signer();
                string signedMessage = signer.SignAMessage(NbReceived, secretKeyControl.readSecretKey(), secretKeyControl.readPrivateKey());
                //encrypt it
                Encrypter encrypter = new Encrypter();
                encryptedSignedMessage = encrypter.EncryptString(signedMessage, publicKeyControl.getPublicKey(friendKeyID), true);
            }
            catch
            {
                //send message 3 (fake)
                //signal that this is failed authentication
                encryptedSignedMessage = "trashEncryptedMessage3"; 
            }

            byte[] data = System.Text.Encoding.ASCII.GetBytes(encryptedSignedMessage);
            ///write data to the network
            NetStream.Write(data, 0, data.Length);          
        }       
    }
}
